#ifndef TCL_STEREO_DEPTH_H
#define TCL_STEREO_DEPTH_H

#include "native_debug.h"

#include <opencv2/opencv.hpp>
#include "opencv2/ximgproc/edge_filter.hpp"
#include "opencv2/ximgproc/disparity_filter.hpp"

#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <iostream>
#include <thread>
#include <chrono>
#include <time.h>

#include "TCLStereoDepthAlgUtils.h"

using namespace cv;
using namespace std;
using namespace cv::ximgproc;

#define TCL_IMAGE_ROTATE_COUNTER_CLOCKWISE_90 1
#define TCL_IMAGE_ROTATE_CLOCKWISE_90 2
#define TCL_IMAGE_ROTATE_180 3
#define TCL_IMAGE_ROTATE_NONE 4

namespace tcl_depth_video {
	class TCLStereoDepthAlg_prv;

	struct DepthPreview_Intrinsics {
		cv::Mat M1;
		cv::Mat D1;
		cv::Mat M2;
		cv::Mat D2;

		void copy(DepthPreview_Intrinsics i_) {
			i_.M1.copyTo(this->M1);
			i_.D1.copyTo(this->D1);
			i_.M2.copyTo(this->M2);
			i_.D2.copyTo(this->D2);
		};
	};


	struct Param_DepthPreview {
		DepthPreview_Intrinsics intrinsics;
		cv::Size full_size;
		cv::Size crop_size;
		cv::Size cali_size;
		cv::Size depth_size;
		int orientation;
		int max_disp;
		int win_size;
		int skip_cycle;
		bool dump_enabled;
		cv::String dump_path;

		void copy(const Param_DepthPreview & param_) {
			this->intrinsics.copy(param_.intrinsics);
			this->full_size = param_.full_size;
			this->crop_size = param_.crop_size;
			this->cali_size = param_.cali_size;
			this->depth_size = param_.depth_size;
			this->orientation = param_.orientation;
			this->max_disp = param_.max_disp;
			this->win_size = param_.win_size;
			this->skip_cycle = param_.skip_cycle;
			this->dump_enabled = param_.dump_enabled;
			this->dump_path = param_.dump_path;
		}
	};

	void DumpImage(const cv::Mat & im_,
		const cv::String & path,
		const cv::String & prefix,
		const cv::String & uid);

	void *DepthPreview_Thread(void * parent);

	class TCLStereoDepthAlg_prv {
	public:
		TCLStereoDepthAlg_prv();
		~TCLStereoDepthAlg_prv();
		void Init(const Param_DepthPreview & param);
		// focus distance is returned
		float Compute(cv::Mat & depth,
			LPTCL_IMAGE yuv_aux,
			LPTCL_IMAGE yuv_main,
			const cv::String & uid,
			float focus_x,
			float focus_y,
			int flag);
		//float GetFocusDistance(float focus_x,
		//                       float focus_y);
		//int Depth2Disparity(float dist_in_mm);
		//void Disparity2Depth(cv::Mat &depthOut, const cv::Mat &dispIn);

		Param_DepthPreview m_param; // input parameter list
		cv::String uid;
		cv::Ptr<cv::StereoBM> bm;
		cv::Ptr<cv::ximgproc::DisparityWLSFilter> wls_filter;
		std::thread thread_core;
		volatile bool task_done;
		volatile bool session_done;
		volatile bool map_locked;

		float cali_w, cali_h;
		float full_w, full_h;
		float crop_w, crop_h;
		float dept_w, dept_h;
		cv::Size2f crop_cali_size, crop_delta_size;
		float down_scale;
		double scale_aux_x, scale_aux_y;

		int freeze_times;
		ThreadState thread_state;
		cv::Size rectify_size;
		cv::Size wls_size;
		cv::Size size_close;
		cv::Size size_micro;
		float disp_close;
		float disp_micro;
		cv::Mat main_close, aux_close, main_micro, aux_micro;
		cv::Mat depth_s16_close, depth_s16_micro;
		cv::Mat depth_u8_close, depth_u8_micro;

		cv::Mat inputGray1, inputGray2;
		cv::Mat inputU1, inputU2;
		cv::Mat inputV1, inputV2;
		cv::Mat resizeImg1, resizeImg2;
		cv::Mat resizeImg1_T, resizeImg2_T;
		cv::Mat remapImg1, remapImg2;
		cv::Mat cropImg1, cropImg2;
		cv::Mat blurImg1, blurImg2;
		cv::Mat stdImg1, stdImg2;
		cv::Mat aux_grey, main_grey;
		cv::Mat rgb_aux, rgb_main;
		cv::Mat prev, curr;
		bool success_flag;
		float real_max_disp;
		std::vector<float> disp_spa; // standard disparity
		std::vector<cv::Point2f> coord_spa; // normalized

		cv::Mat depth_s16; // depth map of int16
		cv::Mat depth_u8; // depth map of uint8
		cv::Mat depth_out; // output depth map of uint8
		cv::Mat depth_last; // the buffered depth map
		cv::Mat depth_base; // basic depth map for camera-steady interpolation

		cv::Mat R1, P1, R2, P2;
		cv::Mat map11, map12, map21, map22, aux_map_x, aux_map_y;
		//    cv::Mat inv_map1, inv_map2;
			//cv::Mat Q;

		void SetupInput(const LPTCL_IMAGE aux, const LPTCL_IMAGE main);

		// method #1
		int Preprocess();
		int StereoRectify();
		int ComputeDepthImpl(const cv::Mat & main_, const cv::Mat & aux_);
		int ComputeDepth();
		int Postprocess();

		// method #2
		int CalcDenseOptFlow();

		// algorithm state
		bool Available();
	private:
		volatile bool init_success;
	};
};

#endif  // TCL_STEREO_DEPTH_H